using System;
using System.Collections.Generic;
using System.Reflection;
using Common.Unity.Extensions.PropertyMapper.Policies;
using Microsoft.Practices.ObjectBuilder2;
using Common.Core;
using Microsoft.Practices.Unity.Utility;

namespace Common.Unity.Extensions.PropertyMapper.Policies
{
	class ReflectionPropertyMappingPolicy : PropertyMappingPolicyBase
	{
		readonly Dictionary<PropertyMappingKey, IEnumerable<PropertyExpressionMapping>> cache = new Dictionary<PropertyMappingKey, IEnumerable<PropertyExpressionMapping>>();

		IEnumerable<PropertyExpressionMapping> ResolveMappings( PropertyMappingKey key )
		{
			if ( !cache.ContainsKey( key ) )
			{
				cache.Add( key, CreateMappings( key ) );
			}
			return cache[key];
		}

		static IEnumerable<PropertyExpressionMapping> CreateMappings( PropertyMappingKey key )
		{
			Pair<Type, Type> first = Pair.Make( key.Source, key.TargetBuildKey.Type );
			foreach ( Pair<Type, Type> pair in new Pair<Type,Type>[]{ first, Pair.Make( key.TargetBuildKey.Type, key.Source ) } )
			{
				foreach ( PropertyInfo info in pair.First.GetProperties( CommonBindingFlags.AllProperties ) )
				{
					PropertyMappingAttribute attribute = info.GetAttribute<PropertyMappingAttribute>();
					if ( attribute != null && attribute.TargetType.IsAssignableFrom( pair.Second ) )
					{
						bool normal = pair == first;
						yield return new PropertyExpressionMapping( normal ? info.Name : attribute.Expression, normal ? attribute.Expression : info.Name );
					}
				}
			}
		}

		protected override IEnumerable<IPropertySetterInfo> ResolveProperties( PropertyMappingContext context )
		{
			foreach ( PropertyExpressionMapping mapping in ResolveMappings( context.Key ) )
			{
				yield return mapping.CreateProperty( context );
			}
		}
	}
}